package educrypt.gui.ctr;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFileChooser;
import javax.swing.SwingWorker;

import educrypt.analysis.Frequencies;
import educrypt.analysis.FrequencyAnalysis;
import educrypt.analysis.FrequencyPair;
import educrypt.commons.Alphabet;
import educrypt.commons.Text;
import educrypt.database.EduCryptDatabase;
import educrypt.database.dao.AlphabetDAO;
import educrypt.database.dao.EquivalencesDAO;
import educrypt.database.dao.FrequencyAnalysisDAO;
import educrypt.database.dao.TextLanguageDAO;
import educrypt.database.dao.TextTypeDAO;
import educrypt.exceptions.EduCryptException;
import educrypt.filter.Equivalences;
import educrypt.filter.Filter;
import educrypt.gui.view.AnalysisOptionsView;
import educrypt.gui.view.AnalysisResultView;
import educrypt.gui.view.AnalysisView;
import educrypt.gui.view.EditorTextOptionsView;
import educrypt.gui.view.EducryptFileChooserView;
import educrypt.gui.view.MainView;
import educrypt.gui.view.TextFilterView;
import educrypt.gui.view.TextOptionsView;
import educrypt.gui.view.TextPreviewView;
import educrypt.gui.view.TextView;
import educrypt.i18n.Messages;
import educrypt.io.CSVFile;
import educrypt.io.FileUtil;
import educrypt.io.Input;
import educrypt.io.Output;

public class AnalysisCtr implements ActionListener, Observer {

	// View
	private MainView view;
	private TextView textView;
	private TextOptionsView textOptionsView;
	private AnalysisOptionsView optionsView;
	private AnalysisResultView resultView;
	private EditorTextOptionsView editorTextTypeView, editorTextLanguageView;
	private TextPreviewView textPreviewView;
	private TextFilterView textFilterView;

	// Model
	private Text text;
	private List<Alphabet> alphabets;
	private List<String> textTypes;
	private List<String> textLanguages;
	private FrequencyAnalysisDAO frequencyAnalysisDAO;

	private CSVFile analysisCsvResults;

	public AnalysisCtr(MainView view) {

		// View
		AnalysisView analysisView = view.getAnalysisPanel(); // tmp

		// Panels
		this.view = view;
		this.textView = analysisView.getTextPanel();
		this.textOptionsView = analysisView.getTextOptionsPanel();
		this.textOptionsView.setAnalysisContext();
		this.optionsView = analysisView.getOptionsPanel();
		this.resultView = analysisView.getResultPanel();

		// Dialogs
		this.editorTextTypeView = view.getEditorTextTypeDialog();
		this.editorTextLanguageView = view.getEditorTextLanguageDialog();
		this.textPreviewView = view.getTextPreviewDialog();
		this.textFilterView = analysisView.getOptionsPanel()
				.getTextFilterDialog();

		this.initializeValues();
	}

	private void initializeValues() {
		// Model
		this.alphabets = AlphabetDAO.getInstance().findAll();
		this.textTypes = TextTypeDAO.getInstance().findAll();
		this.textLanguages = TextLanguageDAO.getInstance().findAll();
		this.frequencyAnalysisDAO = FrequencyAnalysisDAO.getInstance();

		this.analysisCsvResults = new CSVFile();

		// Model<->View
		List<String> alphabetsName = new ArrayList<String>(alphabets.size());
		for (Alphabet alphabet : alphabets) {
			alphabetsName.add(alphabet.getName());
		}
		this.optionsView.showAlphabets(alphabetsName);
		this.textOptionsView.showTypes(textTypes);
		this.textOptionsView.showLanguages(textLanguages);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		try {

			String command = e.getActionCommand();
			// Text
			if (command.equals(TextView.FILE_BROWSE)) {
				textView.showFileChooser();
			}

			if (command.equals(TextView.PREVIEW)) {
				textPreviewView.showPreview(text.getContent());
			}

			// Text options
			if (command.equals(TextOptionsView.TYPE_EDITOR)) {
				editorTextTypeView.open((Component) this.textOptionsView);

			}
			if (command.equals(TextOptionsView.LANGUAGE_EDITOR)) {
				editorTextLanguageView.open((Component) this.textOptionsView);
			}

			// Options
			if (command.equals(AnalysisOptionsView.ALPHABET_SELECTED)) {
				this.processAlphabetSelected();
			}

			if (command.equals(AnalysisOptionsView.ADD_ALPHABET)) {
				this.view.getEditorAlphabetDialog().setSelectedCipherGroup(0);
				this.view.getEditorAlphabetDialog().open(
						(Component) optionsView);
			}

			if (command.equals(AnalysisOptionsView.ANALYZE)) {
				this.processAnalyze();
			}

			if (command.equals(AnalysisOptionsView.FILTER_TEXT)) {
				// Getting equivalences from database
				Equivalences equivalences = EquivalencesDAO.getInstance()
						.findEquivalences();

				// Getting unknown symbols from text
				List<Character> unknownSymbols = Filter.getTextUnknownSymbols(
						this.text, this.alphabets.get(optionsView
								.getSelectedAlphabetIndex() - 1));

				// Adding unknown symbols to equivalences
				equivalences.addNewSymbols(unknownSymbols);

				// Getting positions in the equivalences of the unknown symbols
				List<Integer> symbolsPosition = equivalences
						.getSymbolsPositions(unknownSymbols);

				// Showing equivalences
				textFilterView
						.showTableValues(equivalences.getAllSymbols(),
								equivalences.getAllEquivalentSymbols(),
								symbolsPosition);

				textFilterView.open((Component) optionsView);
			}

			// Filter Dialog
			if (command.equals(TextFilterView.DO_FILTER)) {
				// Saving equivalences into the database
				List<Character> symbols = textFilterView.getSymbols();
				List<Character> equivalentSymbols = textFilterView
						.getEquivalences();
				Equivalences eq = new Equivalences(symbols, equivalentSymbols);
				EquivalencesDAO.getInstance().createEquivalences(eq);

				// Filtering text
				this.text = Filter.filterText(this.text, this.alphabets
						.get(optionsView.getSelectedAlphabetIndex() - 1), eq);
			}

			// Results
			if (command.equals(AnalysisResultView.SAVE_RESULT)) {
				resultView.getSaveFileDialog().showSaveDialog();
			}

			// LoadView - SaveView
			if (command.equals(JFileChooser.APPROVE_SELECTION)) {
				JFileChooser fileChooser = (JFileChooser) e.getSource();
				int type = fileChooser.getDialogType();
				if (type == JFileChooser.OPEN_DIALOG) {
					this.processOpenFile();
				} else if (type == JFileChooser.SAVE_DIALOG) {
					this.processSaveResultToFile();
				}
			}
		} catch (EduCryptException ex) {
			view.showExceptionDialog(ex.getMessage());
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void processSaveResultToFile() {
		EducryptFileChooserView saveFileView = resultView.getSaveFileDialog();
		File selectedFile = saveFileView.getSelectedFile();
		// String frequencies = resultView.getFrequencies();
		Output.writePlainText(analysisCsvResults.getCSV(),
				selectedFile.getAbsolutePath());
	}

	private void processOpenFile() throws EduCryptException {
		// Obtaining file path and name
		EducryptFileChooserView fileChooser = textView.getFileChooser();
		File selectedFile = fileChooser.getSelectedFile();
		String filePath = selectedFile.getAbsolutePath();

		// Creating new text
		String fileName = FileUtil.getFileNameFromPath(filePath);
		String textName = FileUtil.removeExtension(fileName);
		String textContent = Input.readTextFromFile(filePath);
		text = new Text();
		text.setName(textName);
		text.setContent(textContent);

		// Modifying view
		textView.setFilePath(filePath);
		textView.enablePreview(true);
		textOptionsView.setPanelEnabled(true);
		optionsView.setPanelEnabled(true);
		resultView.setPanelEnabled(false);
	}

	private void processAlphabetSelected() {
		if (optionsView.getSelectedAlphabetIndex() > 0) {
			// String alphabetName = optionsView.getSelectedAlphabetName();
			optionsView.enableFilter(true);
			optionsView.enableAnalysis(true);
		} else {
			optionsView.enableFilter(false);
			optionsView.enableAnalysis(false);
		}
	}

	private void processAnalyze() throws EduCryptException {
		assert optionsView.getSelectedAlphabetIndex() > 0;

		if (textOptionsView.getSelectedLanguageIndex() > 0
				&& textOptionsView.getSelectedTypeIndex() > 0) {

			int ngramSize = optionsView.getSelectedNgramSize();
			int alphabetIndex = optionsView.getSelectedAlphabetIndex() - 1;

			Alphabet alphabet = alphabets.get(alphabetIndex);

			// Update text info
			String textType = textOptionsView.getSelectedType();
			String textLanguage = textOptionsView.getSelectedLanguage();
			text.setType(textType);
			text.setLanguage(textLanguage);

			AnalyzeWorker analyzeWorker = new AnalyzeWorker(alphabet,
					ngramSize, text);
			analyzeWorker.execute();
			view.showWaitDialog(true);
		} else {
			throw new EduCryptException(
					Messages.getString("msgSelectTextCharacteristics"));
		}
	}

	private void processAnalyzeFinished(FrequencyAnalysis analysis) {
		// Save text and results in the Database
		frequencyAnalysisDAO.create(analysis);

		analysisCsvResults = new CSVFile();
		// Initialize the csv "columns"
		analysisCsvResults.addRow(new String[] {
				Messages.getString("guiAnalysisResultSymbol"),
				Messages.getString("guiAnalysisResultFrequency"),
				Messages.getString("guiAnalysisResultPercentage") });

		// Generate and show chart/textResults
		resultView.initializeResult(this.text.getName());

		Frequencies fq = analysis.getFrequencies();
		for (FrequencyPair pair : fq) {
			String symbol = pair.getSymbols();
			int absoluteFrequency = fq.getAbsoluteFrequency(symbol);
			resultView.addResult(pair.getSymbols(), pair.getPercentage(),
					absoluteFrequency);

			// Save in the csv format
			DecimalFormat twoDecimal = new DecimalFormat("#.##");
			DecimalFormat zeroDecimal = new DecimalFormat("#");

			analysisCsvResults.addRow(new String[] { pair.getSymbols(),
					zeroDecimal.format(absoluteFrequency),
					twoDecimal.format(pair.getPercentage()) });
		}
		resultView.setPanelEnabled(true);
		resultView.showResult(true);

		view.showWaitDialog(false);
	}

	private class AnalyzeWorker extends SwingWorker<FrequencyAnalysis, Void> {

		private Alphabet alphabet;
		private int ngramSize;
		private Text text;

		public AnalyzeWorker(Alphabet alphabet, int ngramSize, Text text) {
			this.alphabet = alphabet;
			this.ngramSize = ngramSize;
			this.text = text;
		}

		@Override
		protected FrequencyAnalysis doInBackground() throws Exception {
			// Analysis to get frequencies
			FrequencyAnalysis analysis = new FrequencyAnalysis(text, alphabet,
					ngramSize);

			// Save in Database
			FrequencyAnalysisDAO.getInstance().create(analysis);

			return analysis;
		}

		@Override
		protected void done() {
			try {
				FrequencyAnalysis analysis = get();
				processAnalyzeFinished(analysis);
			} catch (Exception e) {
				view.showWaitDialog(false);
				view.showExceptionDialog(e.getLocalizedMessage());
				e.printStackTrace();
			}
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public void update(Observable o, Object arg) {
		if (o instanceof TextTypeDAO) {
			// this.textTypes = ((TextTypeDAO) o).findAll();
			// textOptionsView.showTypes(textTypes);
			this.textTypes = (List<String>) arg;
			textOptionsView.showTypes(textTypes);

		}
		if (o instanceof TextLanguageDAO) {
			List<String> textLanguages = (List<String>) arg;
			textOptionsView.showLanguages(textLanguages);
		}
		if (o instanceof AlphabetDAO) {
			updateAlphabets();
		}

		if (o instanceof EduCryptDatabase) {
			this.textTypes = TextTypeDAO.getInstance().findAll();
			textOptionsView.showTypes(textTypes);

			this.textLanguages = TextLanguageDAO.getInstance().findAll();
			textOptionsView.showLanguages(textLanguages);

			updateAlphabets();

		}
	}

	private void updateAlphabets() {
		alphabets = AlphabetDAO.getInstance().findAll();
		List<String> alphabetNames = new ArrayList<String>();
		for (Alphabet alphabet : alphabets) {
			alphabetNames.add(alphabet.getName());
		}
		optionsView.showAlphabets(alphabetNames);
	}
}
